#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 
#include <arpa/inet.h>
#include <ncurses.h>
#include "NetworkConnector.h"
#include "../Logger/Logger.h"
const int NetworkConnector::BUFFER_LENGTH = 1024;

NetworkConnector::NetworkConnector():settings_(Settings::getInstance()), confirmedSeat(0), connected(false)
{
  portNum = 51717;
  log(portNum, DEBUG);
}

NetworkConnector::~NetworkConnector()
{
  disconnect();
}





int NetworkConnector::sendMessageToServer(const std::string &message)
{
  bzero(buffer,BUFFER_LENGTH);
  log("send game message:");
  log(message);
  strcpy(buffer, message.c_str());
  //int n = write(sockfd,buffer,strlen(buffer));
  int n = send(sockfd, buffer, strlen(buffer), 0);
  if (n <= 0) 
  {
     log("ERROR writing to socket");
  }
  log("N is");
  log(n, DEBUG);
  return n;
}

int NetworkConnector::openCard(int playerNum)
{
  cmaker.setMaker(OPEN_CARD, playerNum, "");
  return sendMessageToServer(cmaker.makeCommand());
}

//some of players gets totem
int NetworkConnector::getTotem(int playerNum)
{
  cmaker.setMaker(GET_TOTEM, playerNum, "");
  return sendMessageToServer(cmaker.makeCommand());
}


int NetworkConnector::connectToServer(const std::string &serverAddr)
{
  log("CONNECTING TO SERVER");
  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if (sockfd < 0)
  {
    log("Error opening socket");
    return 1;
  }
 

  
  //bzero((char *) &serv_addr, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_port = htons(portNum);
  log(serverAddr);
  //bcopy(serverAddr.c_str(), (char *)&serv_addr.sin_addr.s_addr, strlen(serverAddr.c_str()));
  serv_addr.sin_addr.s_addr = inet_addr(serverAddr.c_str());
  log("try to connect");
  if (::connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) 
  {
    log("Error connecting");
    return 2;
  }
  connected = true;
  log("all right");
  return 0;
}

int NetworkConnector::connect(const std::string &name, const time_t &connectTime, int &error)
{
  cmaker.setMaker(CONNECT, 0, name);
  return sendMessageToServer(cmaker.makeCommand());
}

int NetworkConnector::getMessage()
{

  if (buffer[0] == 0)
    return 0;

  parser.setBuffer(buffer);
  log("GETTING_MESSAGE");
  log(buffer);
  while(parser.parseBuffer() == 0)
  {
    int error = 0;
    Command command = parser.getCommand();
    
    if (command == CONNECT)
    {
      log("CONNECT");
      Connector::connect(parser.getPlayerName(), time(0), error);
    }
    else if (command == CONNECT_CONFIRM)
    {
      log("CONNECT_CONFIRM");
      Connector::connect(parser.getPlayerName(), time(0), error);
      confirmedSeat = parser.getPlayerNumber();
    }
    else if (command == START)
    {
      log("START");
      if (confirmedSeat == 0)
      {
        disconnect();
        return 1;
      }
      Connector::startGame(false);
      Connector::setCards(parser.getCards());
      return 0;
    }
    else if (command == OPEN_CARD)
    {
      log("OPEN_CARD");
      Connector::openCard(parser.getPlayerNumber());
    }
    else if (command == GET_TOTEM)
    {
      log("GET_TOTEM");
      Connector::getTotem(parser.getPlayerNumber());
    }
    else if (command == QUIT)
    {
      log("QUIT");
      disconnect();
      clear();
      refresh();
      move(0,0);
      printw("%s leaved the game. Game over", (model_->getPlayerName(parser.getPlayerNumber())).c_str());
      refresh();
      getch();
      return 1;
    }
  }
}

int NetworkConnector::getPlayersFromServer()
{
  //confirmedSeat = 0;
  zeroBuffer();
  //int n = read(sockfd,buffer,255);
  int n = recv(sockfd, buffer, BUFFER_LENGTH, 0);
  //log("Message recieved:");
  //log(buffer);
  if (n < 0)
  { 
    log("ERROR reading from socket", CRITICAL); 
  }
  int err = getMessage();
  if (model_->isGameStarted())
  {
    return 1;
  }
  return err;
}

void NetworkConnector::zeroBuffer()
{
  bzero(buffer,BUFFER_LENGTH);
}

int NetworkConnector::getConfirmedSeat()
{
  return confirmedSeat;
}

char NetworkConnector::getMove(bool &toExit)
{
  FD_ZERO(&readset);
  FD_SET(sockfd, &readset);
  FD_SET(STDIN_FILENO, &readset);
  
  timeval timeout;
  timeout.tv_sec = 15;
  timeout.tv_usec = 0;

  int mx = std::max(sockfd, STDIN_FILENO);
 
  if(select(mx + 1, &readset, NULL, NULL, &timeout) <= 0)
  {
    //log("selectError");
    return 0;
  }
  
  if(FD_ISSET(sockfd, &readset))
  {
    zeroBuffer();
    
    int n = recv(sockfd, buffer, BUFFER_LENGTH, 0);
    log("Game message recieved:");
    log(buffer);
    if (n <= 0)
    { 
      log("ERROR reading from socket"); 
      quit(0);
      toExit = true; 
      return 0;
    }
    
    toExit = getMessage();
  }
  
  if(FD_ISSET(STDIN_FILENO, &readset))
  {
    char c = getch();
    return c;
  }
  return 0;
}

void NetworkConnector::quit(int playerNum)
{
   cmaker.setMaker(QUIT, playerNum, "");
   std::string command = cmaker.makeCommand();

   sendMessageToServer(command);
  
  
}

void NetworkConnector::disconnect()
{
   if (connected)
   {
     ::close(sockfd);
   }
   
   
   connected = false;
}
